﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Internal.Util;
using NGuice.Inject.Spi;
using System.Reflection;

namespace NGuice.Inject.Internal
{
    //complete！
    /// <summary>
    /// 通过类型的成员注入器
    /// </summary>
    internal sealed class MembersInjectorStore
    {
        private readonly InjectorImpl injector;

        private readonly ImmutableList<TypeListenerBinding> typeListenerBindings;

        /// <summary>
        /// value为MembersInjectorImpl类型，Type为一种泛型的类型例如List&ltstring&gt
        /// </summary>
        private readonly FailableCache<Type, object> cache;

        internal class ListenersCache : FailableCache<Type, object>
        {

            private MembersInjectorStore injectorStore;

            public ListenersCache(MembersInjectorStore injectorStore)
            {
                this.injectorStore = injectorStore;
            }
            protected override object Create(Type key, Errors errors)
            {
               MethodInfo method = this.injectorStore.GetType()
                    .GetMethod("CreateWithListeners", BindingFlags.Instance | BindingFlags.NonPublic);
               return method.MakeGenericMethod(key)
                            .Invoke(this.injectorStore, new object[] { key, errors });
                //return injectorStore.CreateWithListeners<Type>(key, errors);
            }
        }

        internal MembersInjectorStore(InjectorImpl injector, IList<TypeListenerBinding> typeListenerBindings)
        {
            this.injector = injector;
            this.typeListenerBindings = ImmutableList<TypeListenerBinding>.CopyOf<TypeListenerBinding>(typeListenerBindings);
            this.cache = new ListenersCache(this);
        }

        /// <summary>
        /// 如果有一个类型监听器被安装，则返回true。
        /// </summary>
        /// <returns></returns>
        public bool HasTypeListeners()
        {
            return !typeListenerBindings.IsEmpty;
        }

        /// <summary>
        /// 获取一个新的完整的具有注入监听器注册的注入器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="errors"></param>
        /// <returns></returns>
        public MembersInjectorImpl<T> Get<T>(Type key, Errors errors)
        {
            object obj = cache.Get(key, errors);
            return (MembersInjectorImpl<T>)obj;
        }

        /// <summary>
        /// 将一个类型（泛型）从缓存中剔除。仅仅在类型不实际对绑定有效时并需要被剔除时调用
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public bool Remove(Type type)
        {
            return cache.Remove(type);
        }

        /// <summary>
        /// 创建一个新的成员注入器且关联注入监听器和方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="type"></param>
        /// <param name="errors"></param>
        /// <returns></returns>
        /// <exception cref="ErrorsException"></exception>
        private MembersInjectorImpl<T> CreateWithListeners<T>(Type type, Errors errors)
        {
            int numErrorsBefore = errors.Size;

            ISet<InjectionPoint> injectionPoints;
            try
            {
                injectionPoints = InjectionPoint.ForInstanceMethodsAndFields(type);
            }
            catch (ConfigurationException e)
            {
                errors.Merge(e.ErrorMessages);
                injectionPoints = e.GetPartialValue<ISet<InjectionPoint>>();
            }
            ImmutableList<SingleMemberInjector> injectors = GetInjectors(injectionPoints, errors);
            errors.ThrowIfNewErrors(numErrorsBefore);

            EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups);
            foreach (TypeListenerBinding typeListener in typeListenerBindings)
            {
                if (typeListener.TypeMatcher.Matches(type))
                {
                    try
                    {
                        typeListener.Listener.Hear<T>(encounter);
                    }
                    catch (Exception e)
                    {
                        errors.ErrorNotifyingTypeListener(typeListener, type, e);
                    }
                }
            }
            encounter.Invalidate();
            errors.ThrowIfNewErrors(numErrorsBefore);

            return new MembersInjectorImpl<T>(injector, encounter, injectors);
        }

        /// <summary>
        /// 为特定的注入点返回注入器：SingleFieldInjector或SingleMethodInjector
        /// </summary>
        /// <param name="injectionPoints"></param>
        /// <param name="errors"></param>
        /// <returns></returns>
        internal ImmutableList<SingleMemberInjector> GetInjectors(ISet<InjectionPoint> injectionPoints, Errors errors)
        {
            List<SingleMemberInjector> injectors = Lists.NewArrayList<SingleMemberInjector>();
            foreach (InjectionPoint injectionPoint in injectionPoints)
            {
                try
                {
                    Errors errorsForMember = injectionPoint.IsOptional ? new Errors(injectionPoint) : errors.WithSource(injectionPoint);
                    SingleMemberInjector injector = injectionPoint.Member is FieldInfo
                        ? (SingleMemberInjector)new SingleFieldInjector(this.injector, injectionPoint, errorsForMember)
                        : (SingleMemberInjector)new SingleMethodInjector(this.injector, injectionPoint, errorsForMember);
                    injectors.Add(injector);
                }
                catch (ErrorsException ignoredForNow)
                {
                    // ignored for now
                }
            }
            return ImmutableList<SingleMemberInjector>.CopyOf<SingleMemberInjector>(injectors);
        }
    }
}
